#pragma once
#include "Common.hpp"
#include "CentralCache.hpp"
// 每个线程独享的内存池 256kb以下

/*声明*/

class ThreadCache
{
public:
	void* Allocate(size_t size);
	void Deallocate(void* ptr, size_t size); 

	void* FetchFromCentralcache(size_t index, size_t size);
	void ListTooLong(FreeList& list, size_t size);
private:
	FreeList _freeList[NFREELIST];  // 自由链表哈希桶
};

static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;  // TLS 线程的局部存储，让每个线程看到的独一份全局变量 -此处为静态定义-

/*定义*/

// 如果当前线程内存池没有内存了，就去中心缓存区获取
void* ThreadCache::FetchFromCentralcache(size_t index, size_t size)
{
	// 首先查看当前字段应该申请多少块 - 使用慢启动策略
	size_t batchNum = min(_freeList[index].GetMaxSize(), SizeClass::NumMoveSize(size));
	if (batchNum == _freeList[index].GetMaxSize()) _freeList[index].GetMaxSize() += 1;
	// 此时向中心缓存申请期望batchNum个对应size类型的内存块
	void* start = nullptr;
	void* end = nullptr;

	size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);

	if (actualNum > 1)
	{
		assert(start != end);
		// 此时将多余的挂在对应哈希桶的链表上
		_freeList[index].PushRange(NextObj(start), end, actualNum - 1);
	}
	return start;
}

// 申请多少字节的内存
void* ThreadCache::Allocate(size_t size)
{
	assert(size <= MAX_BYTES);  // 申请线程内存池的时候最大不可超过256kb
	// 算出申请字节数对应的对齐字节数
	size_t alignSize = SizeClass::RoundUp(size);
	// 对应的自由链表哈希桶桶号
	size_t index = SizeClass::Index(size);

	if (!_freeList[index].Empty()) return _freeList[index].Pop();
	else
	{
		// 从中心存储中去获取
		return FetchFromCentralcache(index, alignSize);
	}
}

// 释放对应对象的内存，并且告知其字节大小
void ThreadCache::Deallocate(void* ptr, size_t size)
{
	assert(ptr && size <= MAX_BYTES);

	size_t index = SizeClass::Index(size);
	_freeList[index].Push(ptr);

	if (_freeList[index].Size() >= _freeList[index].GetMaxSize())
	{
		// 如果此时返回到自由链表的数据已经超过我们之前取数据的批量长度，此时可以返回了
		ListTooLong(_freeList[index], size); // 释放对象的时候，链表过长，回收内存到中心缓存。拿走一个批量即可
	}
}
// 通过对应传过来的超过批量长度的数据，我们回收到上层centralcache中去
void ThreadCache::ListTooLong(FreeList& list, size_t size)
{
	void* start = nullptr;
	void* end = nullptr;
	list.PopRange(start, end, list.GetMaxSize());

	CentralCache::GetInstance()->ReleaseListToSpans(start, size);
}